package com.jxpanda.spring.module.auth.config;


import com.jxpanda.infrastructure.spring.toolkit.SpringContextKit;
import com.jxpanda.spring.module.auth.config.properties.AuthConfigProperties;
import com.jxpanda.spring.module.auth.config.properties.EndpointProperties;
import com.jxpanda.spring.module.auth.config.properties.JwtProperties;
import com.jxpanda.spring.module.auth.config.properties.TokenProperties;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.event.EventListener;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@ComponentScan(basePackages = "com.jxpanda.spring.module.auth.endpoint")
@EnableConfigurationProperties({AuthConfigProperties.class, EndpointProperties.class, TokenProperties.class, JwtProperties.class})
@ImportAutoConfiguration({JwtConfigure.class, TokenConfigure.class, StrategyConfigure.class})
public class AuthModuleAutoConfigure {

    private final AuthConfigProperties authConfigProperties;

    private final EndpointProperties endpointProperties;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity,
                                                         @Autowired(required = false) ReactiveJwtDecoder reactiveJwtDecoder) {

        serverHttpSecurity.authorizeExchange(this::customizeAuthorizeExchange)
                .formLogin(ServerHttpSecurity.FormLoginSpec::disable)
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .cors(ServerHttpSecurity.CorsSpec::disable);

        if (reactiveJwtDecoder != null) {
            serverHttpSecurity.oauth2ResourceServer(resourceServerSpec ->
                    resourceServerSpec.jwt(jwtSpec ->
                            jwtSpec.jwtDecoder(reactiveJwtDecoder)));
        }

        return serverHttpSecurity.build();
    }

    @Bean
    @ConditionalOnMissingBean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置授权规则
     *
     * @param exchanges 授权规则配置单元
     */
    private void customizeAuthorizeExchange(ServerHttpSecurity.AuthorizeExchangeSpec exchanges) {
        exchanges.pathMatchers(mergeOpenUrls())
                .permitAll()
                .anyExchange()
                .authenticated();
    }


    /**
     * 把各个配置中的开放的url合并到一个数组中
     */
    private String[] mergeOpenUrls() {
        List<String> openUrls = authConfigProperties.getOpenUrls();
        openUrls.add(endpointProperties.getOauthToken());
        if (endpointProperties.isEnableJwkSet()) {
            openUrls.add(endpointProperties.getJwkSet());
        }
        return openUrls.toArray(String[]::new);
    }

    /**
     * 监听应用启动完成事件
     */
    @EventListener(ApplicationReadyEvent.class)
    public void onApplicationReady() {
        String oauthToken = endpointProperties.getOauthToken();
        log.info("panda-auth 加载完成，有效的授权端点为：");
        authConfigProperties.getClientRegistrations().stream()
                .map(it -> String.format("[%s] %s", it.getRegistrationId(), oauthToken.replace("{registrationId}", it.getRegistrationId())))
                .forEach(log::info);
    }

}
